package com.karson.service.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.karson.common.config.redis.RedisConfig;
import com.karson.common.dto.rabbitmq.UserModelDTO;
import com.karson.common.enums.EntityType;
import com.karson.common.exception.AsmrException;
import com.karson.common.exception.ExceptionCodeEnum;
import com.karson.common.model.BehaviorWeight;
import com.karson.common.model.UserEntityWeight;
import com.karson.common.request.UserActionRequest;
import com.karson.common.response.AlbumResponse;
import com.karson.common.response.AuthorResponse;
import com.karson.common.util.AsmrUtil;
import com.karson.common.util.RedisUtil;
import com.karson.common.vo.TagVo;
import com.karson.service.dao.AlbumInfoMapper;
import com.karson.service.service.BehaviorWeightService;
import com.karson.service.service.UserEntityWeightService;
import com.karson.service.dao.UserEntityWeightMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @author ZKS
* @description 针对表【ar_user_entity_weight(用户权重记录表（用户画像）)】的数据库操作Service实现
* @createDate 2025-09-22 14:16:26
*/
@Service
public class UserEntityWeightServiceImpl extends ServiceImpl<UserEntityWeightMapper, UserEntityWeight>
    implements UserEntityWeightService{
    @Autowired
    private BehaviorWeightService behaviorWeightService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Transactional(rollbackFor = Exception.class)
    public void createWeight(EntityType entityType, List<Integer> entityIds, Double weight, Integer uid) {
        String weightKey = RedisConfig.USER_WEIGHT_KEY + uid + ":" + entityType.getCode();
        List<UserEntityWeight> userEntityWeightList = new ArrayList<>();

        // 构建待处理权重对象
        entityIds.forEach(id -> {
            // 数位拼接主键Id，避免查询时回表
            long lid = AsmrUtil.generateId(uid, entityType.getCode(), id);
            UserEntityWeight userEntityWeight = updateEntityWeight(lid, id, weight);
            userEntityWeight.setEntityType(entityType.getCode());
            userEntityWeight.setUid(uid);
            userEntityWeight.setId(lid);
            userEntityWeightList.add(userEntityWeight);
        });

        // 1. 获取 Redis 当前最小权重
        Map<Object, Double> latest = redisUtil.getLatest(weightKey, 20);
        double minScore = latest.values().stream()
                .min(Double::compare)
                .orElse(0.0);
        // 2. 批量处理 userEntityWeightList
        redisUtil.executePipeline(connection -> {
            for (UserEntityWeight userEntityWeight : userEntityWeightList) {
                String member = userEntityWeight.getEntityId().toString();
                Double oldScore = redisUtil.zScore(weightKey, Integer.valueOf(member));

                if (oldScore != null) {
                    // 已存在，直接更新权重
                    connection.zAdd(weightKey.getBytes(), userEntityWeight.getWeight(), member.getBytes());
                } else {
                    // 不存在，只有比最小权重大才插入
                    if (userEntityWeight.getWeight() > minScore) {
                        connection.zAdd(weightKey.getBytes(), userEntityWeight.getWeight(), member.getBytes());
                    }
                }
            }
        });
        int cutSize = 100;
        // 3. 裁剪 ZSet，保留前 100 条最高权重，保留 多一点 缓存数据让其更新时更容易命中缓存
        long size = redisUtil.size(weightKey);
        if (size > cutSize) {
            // 删除分数最小的部分
            redisUtil.removeRange(weightKey, 0, size - cutSize);
        }

        // 4. 保存数据库
        saveOrUpdateBatch(userEntityWeightList);
    }
    public UserEntityWeight updateEntityWeight(long lid, Integer entityId, double deltaW) {
        UserEntityWeight userEntityWeight = getById(lid);
        if (userEntityWeight == null) {
            userEntityWeight = new UserEntityWeight();
        }
        userEntityWeight.setEntityId(entityId);
        double v = userEntityWeight.getWeight() + deltaW > 0 ? userEntityWeight.getWeight() + deltaW : 0;
        userEntityWeight.setWeight(
                BigDecimal.valueOf(v)
                        .setScale(2, RoundingMode.HALF_UP)
                        .doubleValue()
        );
        return userEntityWeight;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInterestWeight(UserModelDTO userModelDTO) {
        if (userModelDTO == null) {
            return;
        }
        Integer uid = userModelDTO.getUid();
        if (uid == null){
            throw new AsmrException(ExceptionCodeEnum.UNAUTHORIZED);
        }
        UserActionRequest userAction = userModelDTO.getUserAction();
        // 获取操作对应权重值
        BehaviorWeight weightByAction = behaviorWeightService.getBehaviorWeightByAction(userAction.getActionType());
        if (weightByAction == null){
            return;
        }
        // 从数据库中获取，不再依靠前端传参
        AlbumResponse allAlbumInfoById = albumInfoMapper.getAllAlbumInfoById(userAction.getMessageId());
        if (allAlbumInfoById == null) {
            throw new AsmrException("没有找到该专辑");
        }
        List<Integer> tagList = allAlbumInfoById.getTags().stream().map(TagVo::getId).toList();
        List<Integer> authorList = allAlbumInfoById.getAuthorId().stream().map(AuthorResponse::getId).toList();
        Integer circleId = allAlbumInfoById.getCircleVo().getCircleId();
        Boolean hasAction = userModelDTO.getHasAction();
        double weight = hasAction ? weightByAction.getWeight() * -1 : weightByAction.getWeight();

        if (!tagList.isEmpty()){
            createWeight(EntityType.TAG,tagList,weight,uid);
        }
        if (!authorList.isEmpty()){
            createWeight(EntityType.AUTHOR,authorList,weight,uid);
        }
        if (circleId != null){
            createWeight(EntityType.CIRCLE,List.of(circleId),weight,uid);
        }
    }

}




